import asyncio
import json
import logging
from bleak import BleakScanner, BleakClient
from queue import Queue
from datetime import datetime

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("Bluetooth-Listener")

# ESP32-S3蓝牙设备信息
ESP32_SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"  # 通常是一个自定义的UUID
ESP32_CHARACTERISTIC_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8"  # 用于接收数据的特征UUID

class BluetoothListener:
    def __init__(self, message_queue):
        self.message_queue = message_queue
        self.running = True
        self.connected_devices = set()
        self.clients = {}

    async def scan_for_devices(self):
        """扫描蓝牙设备并尝试连接到ESP32设备"""
        while self.running:
            logger.info("开始扫描蓝牙设备...")
            devices = await BleakScanner.discover()
            
            for device in devices:
                # 检查设备是否已经连接
                if device.address in self.connected_devices:
                    continue
                
                # 尝试连接到设备（这里简化了，实际上应该有更具体的条件来识别ESP32-S3设备）
                logger.info(f"发现设备: {device.name} ({device.address})")
                
                # 尝试连接到设备
                try:
                    # 启动一个新的任务来处理连接
                    asyncio.create_task(self.connect_to_device(device))
                except Exception as e:
                    logger.error(f"连接设备 {device.address} 时出错: {str(e)}")
            
            # 每10秒扫描一次
            await asyncio.sleep(10)

    async def connect_to_device(self, device):
        """连接到蓝牙设备并监听数据"""
        try:
            client = BleakClient(device.address)
            await client.connect()
            
            if client.is_connected:
                logger.info(f"已连接到设备: {device.name} ({device.address})")
                self.connected_devices.add(device.address)
                self.clients[device.address] = client
                
                # 检查设备是否提供我们需要的服务和特征
                services = await client.get_services()
                target_service = None
                target_characteristic = None
                
                for service in services:
                    # 使用粗略匹配或完全匹配UUID
                    if ESP32_SERVICE_UUID.lower() in service.uuid.lower():
                        target_service = service
                        for char in service.characteristics:
                            if ESP32_CHARACTERISTIC_UUID.lower() in char.uuid.lower():
                                target_characteristic = char
                                break
                        if target_characteristic:
                            break
                
                if target_characteristic:
                    # 设置通知回调
                    await client.start_notify(
                        target_characteristic.uuid,
                        lambda sender, data: self.handle_data(device.address, data)
                    )
                    logger.info(f"已开始接收来自 {device.name} 的通知")
                else:
                    # 尝试连接任何可通知的特征
                    for service in services:
                        for char in service.characteristics:
                            if 'notify' in char.properties:
                                try:
                                    await client.start_notify(
                                        char.uuid,
                                        lambda sender, data: self.handle_data(device.address, data)
                                    )
                                    logger.info(f"已连接到特征 {char.uuid}")
                                except Exception as e:
                                    logger.warning(f"连接特征 {char.uuid} 失败: {str(e)}")
                
                # 保持连接直到程序结束
                while self.running and client.is_connected:
                    await asyncio.sleep(1)
                
                # 断开连接
                await client.disconnect()
                logger.info(f"已断开与设备 {device.name} 的连接")
                self.connected_devices.remove(device.address)
                del self.clients[device.address]
        
        except Exception as e:
            logger.error(f"与设备 {device.address} 的连接过程中出错: {str(e)}")
            if device.address in self.connected_devices:
                self.connected_devices.remove(device.address)
            if device.address in self.clients:
                del self.clients[device.address]

    def handle_data(self, device_address, data):
        """处理从设备接收到的数据"""
        try:
            # 将字节数据转换为字符串
            data_str = data.decode('utf-8')
            logger.info(f"接收到来自 {device_address} 的数据: {data_str[:50]}...")
            
            # 将消息放入队列
            self.message_queue.put(data_str)
            
            # 记录接收时间
            now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            logger.info(f"数据接收时间: {now}")
        
        except Exception as e:
            logger.error(f"处理数据时出错: {str(e)}")
            # 如果不是UTF-8编码的字符串，尝试直接将字节作为原始数据处理
            try:
                logger.info(f"尝试作为原始数据处理: {data.hex()}")
                # 可以根据需要实现二进制数据的处理逻辑
            except Exception as e2:
                logger.error(f"处理原始数据时也出错: {str(e2)}")

    async def stop(self):
        """停止监听"""
        self.running = False
        # 断开所有连接
        for address, client in self.clients.items():
            try:
                await client.disconnect()
            except Exception as e:
                logger.error(f"断开设备 {address} 连接时出错: {str(e)}")
        
        logger.info("蓝牙监听器已停止")

# 测试用函数：模拟生成蓝牙数据
async def simulate_bluetooth_data(message_queue):
    """模拟发送蓝牙数据，仅用于测试"""
    import random
    import time
    
    logger.info("启动模拟数据生成器")
    
    # 示例人脸数据
    sample_landmarks = {
        "left_eye": [107, 123],
        "right_eye": [170, 121],
        "nose": [144, 151],
        "mouth_left": [119, 197],
        "mouth_right": [167, 193]
    }
    
    # 样本特征向量
    sample_features = [random.uniform(-1, 1) for _ in range(10)]
    
    while True:
        # 随机变化特征向量
        features = [f + random.uniform(-0.1, 0.1) for f in sample_features]
        
        # 构建测试数据
        data = {
            "detection_result": [[62, 60, 202, 237]],
            "landmarks": sample_landmarks,
            "features": features
        }
        
        # 转换为JSON字符串
        data_json = json.dumps(data)
        
        # 放入队列
        message_queue.put(data_json)
        logger.info(f"模拟数据已生成: {data_json[:50]}...")
        
        # 等待2-10秒
        await asyncio.sleep(random.uniform(2, 10))

async def main(queue):
    """主函数，启动蓝牙监听"""
    listener = BluetoothListener(queue)
    
    # 创建一个扫描任务
    scan_task = asyncio.create_task(listener.scan_for_devices())
    
    # 在开发环境中，可以同时启动模拟数据生成器
    simulate_task = asyncio.create_task(simulate_bluetooth_data(queue))
    
    try:
        # 运行直到手动停止
        await asyncio.gather(scan_task, simulate_task)
    except asyncio.CancelledError:
        # 取消所有任务并停止监听
        scan_task.cancel()
        simulate_task.cancel()
        await listener.stop()

def start_bluetooth_listener(loop, queue):
    """启动蓝牙监听服务的入口函数"""
    # 设置事件循环
    asyncio.set_event_loop(loop)
    
    # 启动主函数
    try:
        loop.run_until_complete(main(queue))
    except Exception as e:
        logger.error(f"蓝牙监听器出错: {str(e)}")
    finally:
        loop.close()

# 如果直接运行此文件，启动测试模式
if __name__ == "__main__":
    test_queue = Queue()
    new_loop = asyncio.new_event_loop()
    start_bluetooth_listener(new_loop, test_queue) 